﻿using Core.DataAccess.Model;
using Core.Framework;
using DotNetty.Framework;
using DotNetty.Framework.BaseTriggers;
using iTool.Connector.CommandOfParameter;
using Grain.Interfaces;
using iTool.ClusterComponent;
using Orleans;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace iTool.Connector.CommandWorkers
{
    [Worker("iTool Cloud")]
    public class RunFunctionWorker : WorkerController
    {
        iToolLogFunctionSaveProvider toolLogFunctionSaveProvider;
        private iToolClusterHostClient iCluster;

        public RunFunctionWorker(iToolLogFunctionSaveProvider toolLogFunctionSaveProvider, iToolClusterHostClient cluster)
        {
            this.toolLogFunctionSaveProvider = toolLogFunctionSaveProvider;
            this.iCluster = cluster;
        }

        [MethodRoute("Run $moduleName")]
        public async Task<DoWorkResponse> Run(HandleDoWorkingContext context, string moduleName)
        {

            var result = new object();

            var parameter = context.Message.GetParameterModel<CloudFunctionParameterOfRuning>();


            if (parameter == null)
            {
                return this.GetErrorResponseOfParameterValidationFailed(context, "parameter is null");
            }

            var channel = context.ChannelHandlerContext.GetChannel();
            // 测查是否使用模块
            if (parameter?.IsUseState == true)
            {
                if (string.IsNullOrWhiteSpace(parameter.ExportedFunctionName))
                    return this.GetErrorResponseOfParameterValidationFailed(context, "ExportedFunctionName is null");

                if (string.IsNullOrWhiteSpace(parameter.StateKey))
                    return this.GetErrorResponseOfParameterValidationFailed(context, "StateKey is null");

                var module = this.iCluster.GetService<ICloudRunningFunctionContainStateService>($"{parameter.StateKey}_{moduleName}");

                result = await module.InvokeExport(parameter.ExportedFunctionName.ToLower(), channel, parameter.Parameters);
            }
            else
            {
                ICloudRunningFunctionService function = this.iCluster.GetService<ICloudRunningFunctionService>(moduleName);

                result =
                    string.IsNullOrWhiteSpace(parameter.ExportedFunctionName) ?
                    await function.Invoke(channel, parameter.Parameters)
                    :
                    await function.InvokeExport(channel, parameter.ExportedFunctionName.ToLower(), parameter.Parameters);
            }

            string outChannel = (result as FunctionResult<dynamic>).Channel;

            if (!string.IsNullOrWhiteSpace(outChannel) && string.IsNullOrWhiteSpace(channel))
            {
                // 用户登录
                string twtToken = await this.BindChannel(this.iCluster, outChannel, context);
                (result as FunctionResult<dynamic>).Token = twtToken;
                return this.GetWorkResponse(context, twtToken, false, result);
            }

            if ((result as FunctionResult<dynamic>).IsChangeChannel)
            {
                // 退出登录
                var channelService = this.iCluster.GetService<IChannelService>(channel);
                await channelService.Offline(AppSetting.iToolConfigure.Ip);
                context.ChannelHandlerContext.UnSubscribeByChannel();

                // 切换用户
                if (!string.IsNullOrWhiteSpace(outChannel))
                {
                    string twtToken = await this.BindChannel(this.iCluster, outChannel, context);
                    (result as FunctionResult<dynamic>).Token = twtToken;

                    // TODU 为了写日志这里先写丑一点，后期改,,,
                    var result1 = this.GetWorkResponse(context, twtToken, false, result);
                    this.LogFunctionDetails(parameter, channel, result as FunctionResult<dynamic>, moduleName);
                    return result1;
                }
                else
                {
                    // 通知客户端退出登录
                    // TODU 为了写日志这里先写丑一点，后期改,,,
                    var result2 = this.GetWorkResponse(context, null, true, result);
                    this.LogFunctionDetails(parameter, channel, result as FunctionResult<dynamic>, moduleName);
                    return result2;
                }
            }

            // TODU 为了写日志这里先写丑一点，后期改,,,
            {
                var result3 = this.GetWorkResponse(context, result);
                this.LogFunctionDetails(parameter, channel, result as FunctionResult<dynamic>, moduleName);
                return result3;
            }
        }


        private async Task<string> BindChannel(iToolClusterHostClient cluster, string channel, HandleDoWorkingContext context)
        {
            // 绑定 Token与channel 关系
            var jwt = new iToolJwtSecurity(AppSetting.iToolConfigure.JWTToken ?? "help-itool@qq.com");
            var token = jwt.BuildToken(channel);


            var iChannel = cluster.GetService<IChannelService>(channel);
            List<string> subscribes = await iChannel.GetSubscribes();

            // 给当前上下文绑定订阅信息
            foreach (var item in subscribes)
                context.ChannelHandlerContext.Subscribe(item);

            // 绑定Channel 和Token的关系
            context.ChannelHandlerContext.BindChannel(channel);

            // 上线
            await iChannel.GoOnline(AppSetting.iToolConfigure.Ip);

            return token;
        }


        // 写入执行日志
        private void LogFunctionDetails(CloudFunctionParameterOfRuning parameter, string channel, FunctionResult<dynamic> result, string moduleName)
        {
            var log = new LogFunction
            {
                host = AppSetting.iToolConfigure.Ip,
                port = AppSetting.iToolConfigure.Port,
                channel = channel,
                parameter = result.ChannelParameter,
                session = result.ChannelSession,
                requestModuleName = moduleName,
                requestFunctionName = parameter.ExportedFunctionName,
                requestBody = parameter.Parameters.TryToJson(),
                isUseState = parameter.IsUseState,
                runTimeTotalMilliseconds = result.RunTimeTotalMilliseconds,
                responseState = result.IsFinished ? 200 : 400,
                responseBody = result.TryToJson(),
                createTime = DateTime.Now
            };

            this.toolLogFunctionSaveProvider.InsertItem(log);
        }

    }
}